//
// Created by baigui on 2021/10/18.
//

#include "TsDemux.h"
#include "common.h"

#include <stdio.h>
#include <ts/PESContext.h>
#include <FFMPEGDemux.h>
extern "C" {
#include <libavutil/crc.h>
#include <libavutil/intreadwrite.h>
}
TsDemux::~TsDemux(){

}

int TsDemux::DetectPacketSize()
{
    uint8_t *p_peek;
    i_packet_header_size = 0;
    if( mFileDatasource->sp_stream_Peek(&p_peek, TS_PACKET_SIZE_MAX ) < TS_PACKET_SIZE_MAX )
        return SP_EGENERIC;

    for( int i_sync = 0; i_sync < TS_PACKET_SIZE_MAX; i_sync++ )
    {
        if( p_peek[i_sync] != 0x47 )
            continue;

        /* Check next 3 sync bytes */
        int i_peek =TS_PACKET_SIZE_MAX * 3 + i_sync + 1;
        if( ( mFileDatasource->sp_stream_Peek(&p_peek, i_peek ) ) < i_peek )
        {
            LOGV( "cannot peek" );
            return -1;
        }
        hexdump_data(p_peek,0,i_peek);
        if( p_peek[i_sync + 0 * TS_PACKET_SIZE_188] == 0x47 &&
            p_peek[i_sync + 1 * TS_PACKET_SIZE_188] == 0x47 &&
            p_peek[i_sync + 2 * TS_PACKET_SIZE_188] == 0x47 )
        {
            return TS_PACKET_SIZE_188;
        }
        else if( p_peek[i_sync + 0 * TS_PACKET_SIZE_192] == 0x47 &&
                 p_peek[i_sync + 1 * TS_PACKET_SIZE_192] == 0x47 &&
                 p_peek[i_sync + 2 * TS_PACKET_SIZE_192] == 0x47 )
        {
            if( i_sync == 4 )
            {
                i_packet_header_size = 4; /* BluRay TS packets have 4-byte header */
            }
            return TS_PACKET_SIZE_192;
        }
        else if( p_peek[i_sync + 1 * TS_PACKET_SIZE_204] == 0x47 &&
                 p_peek[i_sync + 2 * TS_PACKET_SIZE_204] == 0x47 &&
                 p_peek[i_sync + 3 * TS_PACKET_SIZE_204] == 0x47 )
        {
            return TS_PACKET_SIZE_204;
        }
    }

    LOGV("TS module discarded (lost sync)" );
    return -1;

}

TsDemux::TsDemux(weak_ptr<MediaPlayer> player) : Demux(player){

}

static void write_section_data(TsSectionFilter *tss,
                               const uint8_t *buf, int buf_size, int is_start)
{
//    MpegTSSectionFilter *tss = &tss1->u.section_filter;
    uint8_t *cur_section_buf = NULL;
    int len, offset;

    if (is_start) {
        memcpy(tss->section_buf, buf, buf_size);
        tss->section_index = buf_size;
        tss->section_h_size = -1;
        tss->end_of_section_reached = 0;
    } else {
        if (tss->end_of_section_reached)
            return;
        len = MAX_SECTION_SIZE - tss->section_index;
        if (buf_size < len)
            len = buf_size;
        memcpy(tss->section_buf + tss->section_index, buf, len);
        tss->section_index += len;
    }

    offset = 0;
    cur_section_buf = tss->section_buf;
    while (cur_section_buf - tss->section_buf < MAX_SECTION_SIZE && cur_section_buf[0] != 0xff) {
        /* compute section length if possible */
        if (tss->section_h_size == -1 && tss->section_index - offset >= 3) {
            len = (AV_RB16(cur_section_buf + 1) & 0xfff) + 3;
            if (len > MAX_SECTION_SIZE)
                return;
            tss->section_h_size = len;
        }

        if (tss->section_h_size != -1 &&
            tss->section_index >= offset + tss->section_h_size) {
            int crc_valid = 1;
            tss->end_of_section_reached = 1;

            if (tss->check_crc) {
                crc_valid = !av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, cur_section_buf, tss->section_h_size);
                if (tss->section_h_size >= 4)
                    tss->crc = AV_RB32(cur_section_buf + tss->section_h_size - 4);

                if (!crc_valid) {
                    LOGW("crc not valid");
                    crc_valid = 1;
                }
            }
            if (crc_valid) {
                tss->process(cur_section_buf, tss->section_h_size,is_start);
                if (crc_valid != 1)
                    tss->last_ver = -1;
            }

            cur_section_buf += tss->section_h_size;
            offset += tss->section_h_size;
            tss->section_h_size = -1;
        } else {
            tss->section_h_size = -1;
            tss->end_of_section_reached = 0;
            break;
        }
    }
}

/**
 * 这里主要处理ts的四个包头，
 * sync_byte                                            8
 * transport_error_indicator                            1
 * payload_unit_start_indicator                         3
 * transport_priority                                   1
 * pid                                                  13
 * transport_scrambling_control                         2
 * adaptation_field_control                             2
 * continuity_counter                                   4
 *
 * 在MPEG-2TS中，为了传送打包后长度不足188B（包括包头）的不完整TS，或者为了在系统层插入节目时钟参考PCR字段，需要在TS包中插入可变长度字段的调整字段调整字段则包括对较高层次的解码功能有用的相关信息，
 * 调整字段的格式基于采用若干标志符，以表示该字段的某些特定扩展是否存在。调整字段由1B调整字段长度、不连续指示器、随机存取器、PCR标志符、基本数据流优先级指示器、拼接点标志符、传送专用数据标志、
 * 调整字段扩展标志以及有相应标志符的字段组成。
 *
 *4字节包头 + 调整字段Adaption_field + 1字节pointer_field（payload_unit_start_indicator==1时,）+  数据 data_byte;
 * @param packet
 * @return
 */

int TsDemux::handle_packets( int64_t nb_packets)
{
    uint8_t packet[TS_PACKET_SIZE];
    int64_t packet_num;
    int ret = 0;

    int i;
    LOGV("Skipping after seek\n");
        /* seek detected, flush pes buffer */

//        for (i = 0; i < NB_PID_MAX; i++) {
//            if (pids[i]) {
//                if (pids[i]->type == MPEGTS_PES) {
//                    TsPESFilter *pes = dynamic_cast<TsPESFilter *>(pids[i]);
//
////                    pes->data_index = 0;
////                    pes->state = MPEGTS_SKIP; /* skip until pes header */
//                } else if (pids[i]->type == MPEGTS_SECTION) {
//                    dynamic_cast<TsSectionFilter *>(pids[i])->last_ver = -1;
//                }
//                pids[i]->last_cc = -1;
//                pids[i]->last_pcr = -1;
//            }
//        }

//    stop_parse = 0;
    LOGV("what happen\n");
    packet_num = 0;
    for (;;) {
        packet_num++;
        if (nb_packets != 0 && packet_num > nb_packets) {
            return SP_SUCCESS;
        }

        ret = read_packet(packet,i_packet_size);
        LOGV("read packet %d",ret);
        if (ret <= 0)
            break;
//        hexdump_data(packet,0,ret);
        ret = handle_packet(packet);
        LOGV("handle_packet %d",ret);
        if (ret != SP_SUCCESS)
            break;
    }
    return ret;
}

int TsDemux::handle_packet(const uint8_t *packet)
{
    int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity,
            has_adaptation, has_payload;
    const uint8_t *p, *p_end;

    //确认pid
    pid = AV_RB16(packet + 1) & 0x1fff;
    if (pid && discard_pid(pid))
        return SP_SUCCESS;
    //payload_unit_start_indicator 1：该字段用来表示TS包的有效净荷带有PES包或者PSI数据的情况。
    //当TS包带有PES包数据时，payload_unit_start_indicator具有以下特点：置为1，表示TS包的有效净荷以PES包的第一个字节开始；置为0，表示TS包的开始不是PES包。
    //当TS包带有PSI数据时，payload_unit_start_indicator具有以下特点：置为1，表示TS包带有PSI部分的第一个字节，即第一个字节带有指针pointer_field；置为0，表示TS包不带有一个PSI部分的第一个字节，即在有效净荷中没有指针pointer_field。
    //对于空包的包，payload_unit_start_indicator应该置为0。
    is_start = packet[1] & 0x40;
    LOGV("get pid %d",pid);
    TsBaseFilter* tss = pids[pid];

    if (!tss)
        return SP_SUCCESS;
    current_pid = pid;

    //adaptation_field_control
    // 00：是保留值。
    //01：负载中只有有效载荷。
    //10：负载中只有自适应字段。
    //11：先有自适应字段，再有有效载荷。
    afc = (packet[3] >> 4) & 3;
    if (afc == 0) /* reserved value */
        return 0;

    has_adaptation   = afc & 2;
    has_payload      = afc & 1;
    //第一个包会有包头，为了拼接，包头会再ts包后添加一个0x00，第五位不懂。
    is_discontinuity = has_adaptation &&
                       packet[4] != 0 && /* with length > 0 */
                       (packet[5] & 0x80); /* and discontinuity indicated */

    /* continuity check (currently not used) 包拼接效验*/
    cc = (packet[3] & 0xf);
    expected_cc = has_payload ? (tss->last_cc + 1) & 0x0f : tss->last_cc;
    cc_ok = pid == 0x1FFF || // null packet PID
            is_discontinuity ||
            tss->last_cc < 0 ||
            expected_cc == cc;

    tss->last_cc = cc;
    if (!cc_ok) {
        LOGD("Continuity check failed for pid %d expected %d got %d\n",
               pid, expected_cc, cc);
        if (tss->type == MPEGTS_PES) {
            auto *tmp = dynamic_cast<TsPESFilter *>(tss);
            PESContext *pc = tmp->mPESContext;
            pc->flags |= AV_PKT_FLAG_CORRUPT;
        }
    }

    /**
     * 无意义的效验。
     */
    if (packet[1] & 0x80) {
        LOGV("Packet had TEI flag set; marking as corrupt\n");
        if (tss->type == MPEGTS_PES) {
            auto *tmp = dynamic_cast<TsPESFilter *>(tss);
            PESContext *pc = tmp->mPESContext;
            pc->flags |= AV_PKT_FLAG_CORRUPT;
        }
    }

    p = packet + 4;
    if (has_adaptation) {
        //暂时不重要。
//        int64_t pcr_h;
//        int pcr_l;
//        if (parse_pcr(&pcr_h, &pcr_l, packet) == 0)
//            tss->last_pcr = pcr_h * 300 + pcr_l;
        /* skip adaptation field */
        p += p[0] + 1;
    }
    /* if past the end of packet, ignore */
    p_end = packet + TS_PACKET_SIZE;
    if (p >= p_end || !has_payload)
        return 0;

//    pos = avio_tell(stream->pb);
//    if (pos >= 0) {
//        assert(pos >= TS_PACKET_SIZE);
//        ts->pos47_full = pos - TS_PACKET_SIZE;
//    }

    if (tss->type == MPEGTS_SECTION) {
        if (is_start) {
            /* pointer field present  跟在包头之后，占位8bit，属于有效负载，表示从此字段开始到负载中PSI Section的第一个字节之间的字节数
             * 其实类似于跳过多少个字节，但是因为crc效验，所以要添加进去，大部分为0*/
            len = *p++;
            if (len > p_end - p)
                return 0;

            //无关紧要。
            if (len && cc_ok) {
                /* write remaining section bytes */
                write_section_data(dynamic_cast<TsSectionFilter *>(tss),p, len, 0);
                /* check whether filter has been closed */
                if (!pids[pid])
                    return 0;
            }
            p += len;
            if (p < p_end) {
                write_section_data(dynamic_cast<TsSectionFilter *>(tss),
                                   p, p_end - p, 1);
            }
        } else {
            if (cc_ok) {
                write_section_data(dynamic_cast<TsSectionFilter *>(tss),
                                   p, p_end - p, 0);
            }
        }

        // stop find_stream_info from waiting for more streams
        // when all programs have received a PMT
//        if (scan_all_pmts <= 0) {
//            int i;
//            for (i = 0; i < nb_prg; i++) {
//                if (!prg[i].pmt_found)
//                    break;
//            }
//            if (i == nb_prg && ts->nb_prg > 0) {
//                int types = 0;
//                for (i = 0; i < ts->stream->nb_streams; i++) {
//                    AVStream *st = ts->stream->streams[i];
//                    if (st->codecpar->codec_type >= 0)
//                        types |= 1<<st->codecpar->codec_type;
//                }
//                if ((types & (1<<AVMEDIA_TYPE_AUDIO) && types & (1<<AVMEDIA_TYPE_VIDEO)) || pos > 100000) {
//                    LOGD("All programs have pmt, headers found\n");
//                    ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER;
//                }
//            }
//        }

    } else {
        int ret;
        // Note: The position here points actually behind the current packet.
        if (tss->type == MPEGTS_PES) {
            if ((dynamic_cast<TsPESFilter *>(tss)->process(p, p_end - p,0)) < 0)
                return SP_SUCCESS;
        }
    }

    return SP_SUCCESS;
}

bool TsDemux::Open(void *path1) {

    char         *psz_url = (char*)path1;

    mFileDatasource = new FileDatasource(psz_url);

    i_packet_size = DetectPacketSize();

    LOGE("packet_header_size:  %d",i_packet_size);
    if( i_packet_size < 0 )
        return false;

    mpegts_open_section_filter(PAT_PID,MPEGTS_SECTION,1);


    handle_packets(4);

    return true;
}

bool TsDemux::start(){

    return false;
}

void TsDemux::requestbuffer(){

}

void TsDemux::handle(int what, void *) {

}

int TsDemux::discard_pid(int pid) {
    return 0;
}

int TsDemux::read_packet(uint8_t *packet, int64_t size) {

        int len;
        for (;;) {
            len = mFileDatasource->sp_stream_Read(packet, i_packet_size);
            if (len != i_packet_size)
                return len < 0 ? len : AVERROR_EOF;
            /* check packet sync byte */
            if ((packet)[0] != 0x47) {
                /* find a new packet start */
                LOGV("file err");
                return -1;
            } else {
                break;
            }
        }
        return len;
}

int TsDemux:: mpegts_open_section_filter(int pid,MpegTSFilterType type, int check_crc)
{
    TsBaseFilter *filter;
    if (type == MPEGTS_PES){
        filter = new TsPESFilter();
    } else if (type == MPEGTS_SECTION){
        SectionFilterType type1 = pid == PAT_PID? PAT :PMT;
        auto *tmp = new TsSectionFilter(type1);
        tmp->section_buf = static_cast<uint8_t *>(av_mallocz(MAX_SECTION_SIZE));
        filter = tmp;
        tmp->setSectionFilterCallBack(this);
    }

    pids[pid] = filter;

    filter->type    = type;
    filter->pid     = pid;
    filter->es_id   = -1;
    filter->last_cc = -1;
    filter->last_pcr= -1;

    return 1;
}
void TsDemux::OnPmtCallBack(int pmtPid, MediaStream *stream) {
    LOGE("OnPmtCallBack %d",pmtPid);
}

void TsDemux::OnPatCallBack(int pmtPid, int id) {

    LOGE("OnPatCallBack %d=-=---%d",pmtPid,id);
    mpegts_open_section_filter(pmtPid,MPEGTS_SECTION,1);

}
